Wi‑Fi, dual-homed networking, and SSH hardening
Ubuntu Server 24.04 on the MSI laptop — Ethernet carries public API traffic (static IP); Wi‑Fi is for admin SSH (office LAN / VPN path). This doc covers enabling Wi‑Fi on a headless server and locking SSH to the wireless interface at office cutover.
Target topology
┌─────────────────────────────────────┐
Internet / API │ eth0 static public IP │
clients ─────────►│ Nginx :80 / :443 │
│ (UFW: allow 80, 443 on eth0) │
└─────────────────────────────────────┘
Admins (VPN / ┌─────────────────────────────────────┐
office Wi‑Fi) ───►│ wlan0 private / office network │
│ SSH :22 only on wlan0 │
│ (UFW: allow 22 on wlan0 only) │
└─────────────────────────────────────┘
| Interface | Role | SSH | HTTP/S |
|---|---|---|---|
enp58s0 | Production static IP (this server) | No | Yes (Nginx) |
wlo1 (wlp0s20f3) | Admin access | Yes | No |
Other hosts may use eth0, enp*, wlan0, wlp* — always run ip link first.
This server — interface names
From ip link on msi-laptop-3:
| Name | Notes |
|---|---|
enp58s0 | Ethernet, UP |
wlo1 | Wi‑Fi — primary netplan name |
wlp0s20f3 | Same radio as wlo1 (altname) — use wlo1 in netplan |
If Wi‑Fi shows state DOWN, it is not broken — it just has not been brought up yet.
Runbook — connect Wi‑Fi (repeat in server room)
Use this checklist whenever you join a new wireless network (home lab now, office later). Ethernet config stays in 50-cloud-init.yaml; Wi‑Fi lives in 02-wifi.yaml.
Netplan file strategy
| File | Purpose | Edit when |
|---|---|---|
/etc/netplan/50-cloud-init.yaml | Ethernet (enp58s0) | Static public IP in server room |
/etc/netplan/02-wifi.yaml | Wi‑Fi only (wlo1) | Every new SSID/password |
Netplan merges all files in /etc/netplan/. Do not delete 50-cloud-init.yaml. Add or edit 02-wifi.yaml for Wi‑Fi.
Template: templates/netplan/02-wifi.yaml.template
Step 1 — One-time packages (skip if already installed)
sudo apt update
sudo apt install -y iw wpasupplicant wireless-tools rfkill net-tools linux-firmware firmware-iwlwifi
Step 2 — Scan for SSIDs
Pick the 2.4 GHz SSID when offered (_2.4, 2G, etc.) — better range for admin SSH. Use 5 GHz only if 2.4 is unavailable.
WIFI=wlo1
sudo rfkill unblock wifi
sudo ip link set "${WIFI}" up
sudo iw dev "${WIFI}" scan | grep -E 'SSID:|signal:'
Note the exact SSID string (case and punctuation matter). Example from home setup: Wi-not-Fi?_2.4.
Step 3 — Create or edit Wi‑Fi netplan
sudo nano /etc/netplan/02-wifi.yaml
New network — copy-paste and edit SSID + password:
network:
version: 2
renderer: networkd
wifis:
wlo1:
optional: true
dhcp4: true
access-points:
"YOUR_SSID_EXACTLY_AS_SCANNED":
password: "your-wifi-password"
SSID must be quoted if it contains ?, spaces, or special characters.
Server room — optional static Wi‑Fi IP (recommended before binding SSH to ListenAddress):
network:
version: 2
renderer: networkd
wifis:
wlo1:
optional: true
dhcp4: no
addresses:
- 192.168.x.x/24 # reserve on office AP/router
routes: [] # no default route via Wi‑Fi
nameservers:
addresses: [192.168.x.1]
access-points:
"OFFICE_SSID":
password: "office-wifi-password"
Do not add a default route on Wi‑Fi when Ethernet carries production traffic.
Step 4 — Apply
sudo chmod 600 /etc/netplan/02-wifi.yaml
sudo netplan generate
sudo netplan try
Press Enter within 120 seconds to keep the config. Then:
sudo netplan apply
Step 5 — Verify
ip -4 addr show wlo1
ip route show default
networkctl status wlo1 --no-pager
ping -I wlo1 -c 3 8.8.8.8
Record the Wi‑Fi IP:
ip -4 addr show wlo1 | grep inet
Test SSH from your workstation (via VPN path when in production):
ssh gqc@<wlo1-ip>
Default route should still use Ethernet after office static IP is configured: ip route | grep default.
Step 6 — Update this doc’s table
Fill in Server-specific values at the bottom: SSID, Wi‑Fi IP, date/location.
Server room redo (quick reference)
When the office Wi‑Fi differs from home lab:
- Scan:
sudo iw dev wlo1 scan | grep SSID - Edit
/etc/netplan/02-wifi.yaml— new SSID and password (and static IP if used) sudo netplan try→sudo netplan apply- Confirm
ip -4 addr show wlo1 - Update
ListenAddressin/etc/ssh/sshd_configif Wi‑Fi IP changed - Proceed with UFW + SSH hardening (Phase 2)
To remove old home Wi‑Fi config, edit 02-wifi.yaml in place — only one access-points block is needed unless you want fallback SSIDs.
Phase 1 — Enable Wi‑Fi (detail)
1. Discover hardware
ip link
lshw -C network 2>/dev/null | grep -A12 "Wireless\|Ethernet"
rfkill list
Note the wireless interface name (often wlp2s0, wlan0, etc.). If rfkill shows Soft blocked: yes:
sudo rfkill unblock wifi
2. Install packages and firmware
Headless Server often ships without Wi‑Fi userland tools. MSI laptops usually need Intel firmware.
sudo apt update
sudo apt install -y \
iw \
wpasupplicant \
wireless-tools \
rfkill \
net-tools \
linux-firmware
# Intel Wi‑Fi (common on MSI) — safe to install even if not Intel
sudo apt install -y firmware-iwlwifi
iw is required for manual scans (iw: command not found without it).
Reboot if the interface still does not appear:
sudo reboot
After reboot, confirm the wireless interface exists:
ip link show
3. Scan for networks (sanity check)
Common mistakes:
sudo iw dev wlo1alone prints help — you must include thescansubcommand.- Interface must be UP before scanning (
state DOWNinip linkwill fail or hang). - Use the primary name
wlo1, not only the altnamewlp0s20f3.
Copy-paste for msi-laptop-3:
WIFI=wlo1
sudo rfkill unblock wifi
sudo ip link set "${WIFI}" up
ip link show "${WIFI}"
# expect: state UP
sudo iw dev "${WIFI}" scan | grep -E 'SSID:|signal:'
If scan runs but shows no SSIDs, you may be on 5 GHz-only APs while driver scans 2.4 — try moving closer to the AP or check router SSID.
Alternative scan (sometimes clearer output):
sudo iwlist wlo1 scan | grep -E 'ESSID|Signal level'
# requires wireless-tools (installed above)
If scan fails, check driver/firmware:
rfkill list
dmesg | grep -iE 'iwl|wifi|wlan|firmware' | tail -30
lsmod | grep iwl
If rfkill shows Hard blocked: yes, check physical Wi‑Fi switch / BIOS wireless setting on the laptop.
4. Configure netplan (Wi‑Fi + Ethernet)
See Runbook — connect Wi‑Fi above for the standard 02-wifi.yaml approach.
Below is a single-file alternative (both interfaces in one file) for office static IP cutover.
Ubuntu Server uses Netplan. Find existing files:
ls /etc/netplan/
sudo cat /etc/netplan/*.yaml
On msi-laptop-3 today, Ethernet is only in 50-cloud-init.yaml:
network:
version: 2
ethernets:
enp58s0:
Wi‑Fi belongs in separate 02-wifi.yaml — do not require editing this stub until server room static IP.
Single-file dual-homed example (for later office cutover — adjust IPs, SSID):
# /etc/netplan/01-dual-homed.yaml
# chmod 600 — contains Wi‑Fi password
network:
version: 2
renderer: networkd
ethernets:
enp58s0: # ← msi-laptop-3 wired
dhcp4: no
addresses:
- 203.0.113.10/24 # ← static public/LAN IP (office)
routes:
- to: default
via: 203.0.113.1 # ← default gateway on wired
nameservers:
addresses: [1.1.1.1, 8.8.8.8]
wifis:
wlo1: # ← msi-laptop-3 wireless (not wlp0s20f3)
optional: true # boot continues if Wi‑Fi unavailable
dhcp4: true # or static — see below
access-points:
"OfficeWiFi-SSID":
password: "your-wifi-password"
# Do NOT add default route via Wi‑Fi if Ethernet is primary:
routes: []
routing-policy: [] # avoid Wi‑Fi becoming default when both up
Wi‑Fi static IP (recommended for stable SSH ListenAddress):
wifis:
wlo1:
optional: true
dhcp4: no
addresses:
- 192.168.50.100/24 # ← reserve this on the AP/router
routes: [] # no default via Wi‑Fi
nameservers:
addresses: [192.168.50.1]
access-points:
"OfficeWiFi-SSID":
password: "your-wifi-password"
WPA2-Enterprise (802.1X) — use auth: key-management: 802.1x and identity/password; see Netplan Wi‑Fi docs.
Apply and test (single-file example only):
sudo chmod 600 /etc/netplan/01-dual-homed.yaml
sudo netplan generate
sudo netplan try
# Confirm within 120s — netplan rolls back if you don't accept
sudo netplan apply
ip addr show
ip route show
For 02-wifi.yaml only, use the Step 4 — Apply commands in the runbook.
Verify:
- Ethernet has the static/production address.
- Wi‑Fi has an address on the office/admin subnet.
- Default route goes via Ethernet (for API traffic):
ip route | grep default.
If Wi‑Fi steals the default route, remove default from Wi‑Fi in netplan (only via on eth0).
Phase 2 — Office cutover: UFW + SSH on Wi‑Fi only
Do this when in the office on the same network you will use for admin SSH. Keep a local console session or IPMI open in case of lockout.
Order of operations
- Confirm Wi‑Fi works and you can SSH over Wi‑Fi IP.
- Configure UFW (SSH on
wlan0only; web oneth0). - Configure
sshdListenAddressto Wi‑Fi IP (optional extra layer). - Test new SSH session over Wi‑Fi before closing the old session.
- Enable UFW.
Replace interface names and IPs:
WIFI=wlo1
WIFI_IP=192.168.50.100 # ip -4 addr show dev wlo1
ETH=enp58s0
UFW rules
sudo ufw default deny incoming
sudo ufw default allow outgoing
# SSH — wireless only
sudo ufw allow in on ${WIFI} to any port 22 proto tcp comment 'SSH admin wlan'
# HTTP/S — wired only (public API)
sudo ufw allow in on ${ETH} to any port 80 proto tcp comment 'HTTP public eth'
sudo ufw allow in on ${ETH} to any port 443 proto tcp comment 'HTTPS public eth'
# Do NOT add generic "ufw allow OpenSSH" — that opens 22 on all interfaces
sudo ufw enable
sudo ufw status verbose
Confirm:
sudo ufw status numbered
Port 22 should show on ${WIFI} only; 80/443 on ${ETH} only.
SSH: bind to Wi‑Fi address
Edit sshd config:
sudo nano /etc/ssh/sshd_config
Set (use your Wi‑Fi IPv4):
# Remove or comment any ListenAddress 0.0.0.0 or ListenAddress ::
ListenAddress 192.168.50.100
Port 22
Validate and reload:
sudo sshd -t
sudo systemctl reload ssh
# or: sudo systemctl reload sshd # some installs use ssh.service
From a second terminal (connected via Wi‑Fi IP, not Ethernet):
ssh gqc@192.168.50.100
Only after that succeeds, disconnect the old session.
Recommended sshd hardening (same maintenance window)
Already planned elsewhere — apply together:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
sudo sshd -t && sudo systemctl reload ssh
VPN note
If admins reach the server only through a VPN (WireGuard/OpenVPN) and SSH should listen on the tunnel interface instead of (or in addition to) Wi‑Fi:
ip link | grep -E 'wg|tun'
Then either:
- UFW:
sudo ufw allow in on wg0 to any port 22 proto tcp - sshd:
ListenAddress 10.x.x.x(VPN address on the server)
Wi‑Fi + VPN is common: Wi‑Fi joins office network → VPN client connects → SSH over VPN IP. Pick one admin path and document the chosen IP in the README.
Troubleshooting
| Symptom | Check |
|---|---|
iw: command not found | sudo apt install -y iw |
sudo iw dev wlo1 dumps help only | Add scan: sudo iw dev wlo1 scan |
wlo1 shows state DOWN | sudo rfkill unblock wifi && sudo ip link set wlo1 up |
| No wireless interface | rfkill list; firmware-iwlwifi; dmesg \| grep iwl |
| Interface up, no IP | journalctl -u systemd-networkd -n 50; netplan YAML syntax |
| Wrong default route | ip route; remove default from Wi‑Fi in netplan |
| Locked out of SSH | Local console; fix UFW: sudo ufw disable; fix sshd ListenAddress |
| SSH works on eth, not wlan | UFW may still allow OpenSSH globally — remove and use interface rules |
| Wi‑Fi drops at lid close | Already masked suspend; confirm HandleLidSwitch=ignore in logind |
Netplan debug
sudo netplan --debug apply
networkctl status
networkctl status wlo1
See what is listening
sudo ss -tlnp | grep ':22'
Should show the Wi‑Fi IP only after hardening.
Checklist — Wi‑Fi enable (any location)
- Packages installed (
iw,wpasupplicant,firmware-iwlwifi) - Scan shows target SSID (
sudo iw dev wlo1 scan) -
/etc/netplan/02-wifi.yamlcreated (SSID + password quoted correctly) -
50-cloud-init.yamlleft in place for Ethernet -
sudo netplan try/applysuccessful -
wlo1has IPv4 address - SSH works via Wi‑Fi IP
- Server-specific values table updated below
Checklist — office cutover (with UFW)
- Wi‑Fi IP documented (prefer DHCP reservation or static)
- Second SSH session tested on Wi‑Fi IP
- UFW: 22 on
wlan0only; 80/443 oneth0only - sshd
ListenAddressset to Wi‑Fi IP -
PasswordAuthentication no/PermitRootLogin no(after key auth verified) - Verify SSH fails on Ethernet public IP:
ssh gqc@<eth-static-ip>(expected) - Verify API still reachable on Ethernet :443 from outside (when DNS/SSL ready)
Related docs
- Firewall baseline: README.md (deferred until server room)
- SSH keys (workstation → server): existing server SSH setup
- Power / lid: prior logind configuration
Server-specific values (fill in)
| Item | Home lab | Server room (office) |
|---|---|---|
| Ethernet interface | enp58s0 | enp58s0 |
| Wi‑Fi interface | wlo1 | wlo1 |
| Wi‑Fi SSID | Wi-not-Fi?_2.4 | TBD |
| Wi‑Fi IP (SSH) | TBD | TBD |
| Ethernet static IP | DHCP / stub | 74.83.141.228 |
| Netplan Wi‑Fi file | /etc/netplan/02-wifi.yaml | same file, edit SSID |
| Last updated | TBD | TBD |